package com.qipay.notify.impl;

import com.qipay.baas.model.Cp;
import com.qipay.baas.model.NotifyLog;
import com.qipay.baas.model.NotifyLogExample;
import com.qipay.baas.model.Order;
import com.qipay.cp.CpService;
import com.qipay.mapper.NotifyLogMapper;
import com.qipay.notify.NotifyLogService;
import com.qipay.order.config.OrderChannelEnum;
import com.qipay.scalar.type.NotifyLogStateEnum;
import com.qipay.utils.ResultUtils;
import com.qipay.utils.XmlUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class NotifyLogServiceImpl implements NotifyLogService {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${api.response.sign.name:}")
    String apiResponseSignName;

    @Value("${api.response.key.name:}")
    String apiResponseKeyName;

    private int[] NotifyFailRate = {
            0, 15, 15, 30, 180, 1800, 1800, 1800, 1800, 3600
    };

    @Autowired
    NotifyLogMapper notifyLogMapper;

    @Autowired
    CpService cpService;

    @Override
    @Transactional
    public void setOrderSuccess(Order order, Cp cp) throws IOException {
        NotifyLog log = new NotifyLog();
        log.setNotifyUrl(order.getNotifyUrl());
        log.setOpType((byte) 1);
        log.setState((byte) 0);
        log.setTradeNo(order.getTradeNo());
        log.setFailCount((byte) 0);
        log.setCreateTime(new Date());
        log.setLastNotifyTime(new Date());

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Map<String, Object> msgMaps = new HashMap<>();
        msgMaps.put("version", "1.0");
        msgMaps.put("charset", "UTF-8");
        msgMaps.put("sign_type", "MD5");
        msgMaps.put("status", 0);
        msgMaps.put("result_code", 0);
        msgMaps.put("mch_id", cp.getAppId());
        msgMaps.put("nonce_str", System.currentTimeMillis());
        msgMaps.put("trade_type", OrderChannelEnum.getByType(order.getChannelType()));
        msgMaps.put("pay_result", 0);
        msgMaps.put("transaction_id", order.getTradeNo());
        msgMaps.put("out_trade_no", order.getOutTradeNo());
        msgMaps.put("total_fee", order.getAmount());
        msgMaps.put("fee_type", "CNY");
        msgMaps.put("time_end", simpleDateFormat.format(order.getFinishTime()));

        String msg = XmlUtils.toString(
                ResultUtils.signResult(
                        msgMaps,
                        cp.getSecretId(),
                        apiResponseSignName,
                        apiResponseKeyName),
                "xml"
        );
        log.setNotifyMsg(msg);

        notifyLogMapper.insert(log);
    }

    @Override
    @Transactional
    public List<NotifyLog> getNotifyList(int size) {
        NotifyLogExample notifyLogExample = new NotifyLogExample();
        notifyLogExample.setLimit(size);
        notifyLogExample.setOrderByClause("last_notify_time");
        notifyLogExample.createCriteria().andLastNotifyTimeLessThan(new Date())
                .andStateNotEqualTo(NotifyLogStateEnum.SUCCESS.getType())
                .andStateNotEqualTo(NotifyLogStateEnum.FAIL.getType())
                .andFailCountLessThan((byte) NotifyFailRate.length);
        return notifyLogMapper.selectByExample(notifyLogExample);
    }

    @Override
    @Async
    @Transactional
    public void send(NotifyLog notifyLog) {
        if (notifyLog == null) {
            return;
        }
        if (Objects.equals(notifyLog.getState(), NotifyLogStateEnum.SUCCESS.getType())) {
            return;
        }
        if (notifyLog.getFailCount() >= NotifyFailRate.length) {
            notifyLog.setState(NotifyLogStateEnum.FAIL.getType());
            notifyLogMapper.updateByPrimaryKey(notifyLog);
            return;
        }

        if(StringUtils.equalsIgnoreCase(notifyLog.getNotifyUrl(), "none")) {
            notifyLog.setState(NotifyLogStateEnum.SUCCESS.getType());
            notifyLogMapper.updateByPrimaryKey(notifyLog);
            return;
        }

        if (notifyLog.getOpType() == 1) {
            String resp = sendPost(notifyLog.getNotifyMsg(), notifyLog.getNotifyUrl());
            notifyLog.setRsp(resp);
            if (StringUtils.isNotBlank(resp) && StringUtils.equalsIgnoreCase(resp, "success")) {
                notifyLog.setState(NotifyLogStateEnum.SUCCESS.getType());
            } else {
                notifyLog.setState(NotifyLogStateEnum.UNSUCCESS.getType());
                if (notifyLog.getFailCount() >= NotifyFailRate.length - 1) {
                    notifyLog.setState(NotifyLogStateEnum.FAIL.getType());
                } else {
                    notifyLog.setFailCount((byte) (notifyLog.getFailCount() + 1));
                    notifyLog.setLastNotifyTime(
                            org.apache.commons.lang3.time.DateUtils.addSeconds(
                                    new Date(),
                                    NotifyFailRate[notifyLog.getFailCount()]
                            )
                    );
                }
            }
            notifyLogMapper.updateByPrimaryKey(notifyLog);
        }
    }

    protected String sendPost(String strBody, String url) {
        final HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, 3000);
        HttpConnectionParams.setSoTimeout(httpParams,3000);
        HttpClient client = new DefaultHttpClient(httpParams);

        String result = null;

        HttpPost post = new HttpPost(url);
        try {
            if (null != strBody) {
                logger.info(getClass().getName() +
                        " pay sendPost request body : " +
                        strBody + ", request url:" + url);
                StringEntity entity = new StringEntity(strBody, "utf-8");
                entity.setContentEncoding("UTF-8");
                entity.setContentType("application/xml");
                post.setEntity(entity);
            }

            HttpResponse resultResp = client.execute(post);

            if (resultResp.getStatusLine().getStatusCode() == 200) {
                try {
                    result = EntityUtils.toString(resultResp.getEntity(), "utf8");
                    logger.info(getClass().getName() +
                            "pay sendPost response body : " + result + ", request url:" + url);
                } catch (Exception e) {
                    logger.error(e.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.toString());
        }
        return result;
    }
}
